home *** CD-ROM | disk | FTP | other *** search
Text File | 2001-02-07 | 34.2 KB | 1,168 lines |
- ## -*-Tcl-*-
- # ###################################################################
- # AlphaTcl - core Tcl engine
- #
- # FILE: "filesets.tcl"
- # created: 20/7/96 {6:22:25 pm}
- # last update: 2/7/2001 {9:20:49 PM}
- # Author: Vince Darley
- # E-mail: <vince@santafe.edu>
- # mail: 317 Paseo de Peralta, Santa Fe, NM 87501, USA
- # www: <http://www.santafe.edu/~vince/>
- #
- #==============================================================================
- # Alpha calls two fileset-related routines, 'getCurrFileSet', and
- # 'getFileSetNames'. Alpha will also attempt to set the variable 'currFileSet'
- # on occasion, but this isn't critical.
- #==============================================================================
- #
- # This file, and the interfaces it contains are undergoing some
- # development. The APIs may undergo minor changes in the future,
- # as we learn more about how users want to interact with filesets.
- # Code contributions and suggestions are very welcome.
- # ###################################################################
- ##
-
- alpha::extension filesets 1.0fc1 {
- # Something in here must ensure this file is sourced.
-
- # Build some filesets on the fly.
- set gfileSets(Help) [list [file join $HOME Help *] 3]
- # Declare their types
- set gfileSetsType(Help) "fromHierarchy"
- filesetRegisterProcedural "Open Windows" procFilesetOpenWindows
- filesetRegisterProcedural "Top Window's Folder" procFilesetDirTopWin
- filesetRegisterProcedural "Top Window's Hierarchy" procFilesetHierarchyTopWin
- filesetRegisterProcedural "Recurse in folder…" procFilesetRecurseIn
- lunion varPrefs(Files) currFileSet
- # The current fileset is used as a default for some actions. It may
- # also be updated automatically to reflect the user's most recent
- # fileset-menu selection.
- newPref var currFileSet "Top Window's Folder" global changeFileSet gfileSets array
-
- ##
- # A type is a means of prompting the user and characterising
- # the interface to a type, even though the actual storage may be
- # very simple (a list in most cases).
- ##
- fileset::registerNewType fromDirectory "glob"
- fileset::registerNewType fromHierarchy "fromHierarchy"
- fileset::registerNewType fromOpenWindows "list"
- fileset::registerNewType procedural "procedural"
- fileset::registerNewType recurseIn "procedural"
-
- set fileset::notChangeable [list "Open Windows" \
- "Top Window's Folder" "Top Window's Hierarchy" \
- "Recurse in folder…"]
-
- prefs::modified currFileSet
-
- hook::register preOpeningHook fileset::checkOpeningPreference
-
- # Make sure our preferences are ok.
- foreach fset [array names gfileSets] {
- if {![info exists gfileSetsType($fset)]} {
- lappend fsetErrors $fset
- unset gfileSets($fset)
- prefs::modified gfileSets($fset)
- }
- }
- if {[info exists fsetErrors]} {
- alertnote "[join $fsetErrors {, }] filesets were corrupted, and have\
- been removed"
- unset fsetErrors
- }
- if {[info exists fset]} {unset fset}
- } maintainer {
- "Vince Darley" vince@santafe.edu <http://www.santafe.edu/~vince/>
- } help {file "Filesets Help"}
-
- # Register utilities
-
- set "filesetUtils(browseFileset…)" [list * browseFileset]
- set "filesetUtils(renameFileset…)" [list * renameFileset]
- set "filesetUtils(openEntireFileset…)" [list * openEntireFileset]
- set "filesetUtils(filesetToAlpha…)" [list * filesetToAlpha]
- set "filesetUtils(closeEntireFileset…)" [list * closeEntireFileset]
- set "filesetUtils(replaceInFileset…)" [list * replaceInFileset]
- set "filesetUtils(stuffFileset…)" [list * stuffFileset]
- set "filesetUtils(wordCount)" [list * wordCountFileset]
- set "filesetUtils(openFilesetFolder…)" [list * openFilesetFolder]
-
- # ◊◊◊◊ Filesets API ◊◊◊◊ #
-
- # A fileset has a few key properties:
- #
- # (i) a name
- # (ii) a way of testing whether any given file is in that fileset
- # (iii) a way of listing all files in the fileset
- # (iv) a 'basic type', which is currently any of:
- # 'list'
- # 'procedural'
- # 'glob'
- # 'fromHierarchy'
- #
- # It may also have any number of other properties, usually added by
- # other packages. For instance, the filesets menu will add a flag
- # declaring whether a fileset is shown in the menu.
-
- ##
- # To add a new fileset type, you need to define the following:
- # fileset::registerNewType myType "list"
- # proc fileset::myType::create {} {}
- # proc fileset::myType::updateContents {name {andMenu 0}} {}
- #
- # For more complex types (e.g. the tex-type), also define:
- # proc fileset::myType::selected {fset menu item } {}
- # proc fileset::myType::listFiles {name} {}
- #
- # For filesets you want to make easily editable via the 'editFilesets'
- # dialog, you must also define:
- # proc fileset::myType::getDialogItems {name} {}
- # proc fileset::myType::setDetails {name args} {}
- #
- # These procedures will all be called automatically under the correct
- # circumstances. The purposes of these are as follows:
- #
- # 'create' -- query the user for name etc. and create
- # 'updateContents' -- given the information in 'gfileSets', recalculate
- # the member files.
- # 'selected' -- a member was selected in a menu.
- # 'listFiles' -- given info in all except 'fileSets', return list
- # of files to be stored in that variable.
- # 'makeFileSetAndMenu' -- generate the sub-menu
- #
- # Your code may wish to call 'isWindowInFileset ?win? ?type?' to
- # check if a given (current by default) window is in a fileset of a
- # given type.
- ##
-
-
- namespace eval fileset {}
-
- ##
- # -------------------------------------------------------------------------
- #
- # "fileset::registerNewType" --
- #
- # Add a new type of fileset to the list of known types. Having
- # called this procedure, Alpha will automatically know how to interact
- # with the new fileset type, provided it fulfills the fileset API.
- #
- # This requires the existence of the procs:
- #
- # 'fileset::$type::create'
- # 'fileset::$type::updateContents'
- #
- # Note that the namespace 'fileset::$type' is automatically created
- # by this procedure.
- #
- # Filesets which you want to make editable in the 'Edit Filesets'
- # dialog must also define the procs
- #
- # proc fileset::$type::getDialogItems {name}
- # proc fileset::$type::setDetails {name args}
- #
- # -------------------------------------------------------------------------
- ##
- proc fileset::registerNewType {type kind} {
- global fileset::typeKindMap
- set fileset::typeKindMap($type) $kind
- # Make sure this namespace exists
- namespace eval ::fileset::$type {}
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "fileset::attachNewInformation" --
- #
- # If 'to' is '*' then this item is automatically attached to all filesets,
- # if not then each fileset may individually select whether to attach
- # this information or not. This selection is done by the user, via the
- # 'Attach/Detach info' button in the 'Edit A Fileset' dialog.
- # -------------------------------------------------------------------------
- ##
- proc fileset::attachNewInformation {to infoType name {defaultValue ""} {help ""} {modifiedScript ""}} {
- global fileset::infoTypes gfileSets fileset::infoStorage
- set fileset::infoTypes($name) [list $to $name $infoType $defaultValue $help $modifiedScript]
- foreach fset [array names gfileSets] {
- if {![info exists fileset::infoStorage($fset,$name)]} {
- set fileset::infoStorage($fset,$name) $defaultValue
- if {[string length $modifiedScript]} {
- eval $modifiedScript [list $fset $defaultValue]
- }
- }
- }
- }
-
- proc fileset::ensureAllInfoAttached {fsets infoNames} {
- global fileset::infoTypes gfileSets fileset::infoStorage
- if {![llength $fsets]} { set fsets [array names gfileSets] }
- if {![llength $infoNames]} { set infoNames [array names fileset::infoTypes] }
- foreach infoName $infoNames {
- set defaultValue [lindex [set fileset::infoTypes($infoName)] 3]
- set modifiedScript [lindex [set fileset::infoTypes($infoName)] 5]
- foreach fset $fsets {
- if {![info exists fileset::infoStorage($fset,$infoName)]} {
- set fileset::infoStorage($fset,$infoName) $defaultValue
- if {[string length $modifiedScript]} {
- eval $modifiedScript [list $fset $defaultValue]
- }
- }
- }
- }
- }
-
- proc fileset::isAttached {fset name} {
- global fileset::attachments
- if {[info exists fileset::attachments($fset,$name)]} {
- return [set fileset::attachments($fset,$name)]
- } else {
- return 0
- }
- }
-
- proc fileset::chooseAttachments {fset} {
- global fileset::infoTypes
- set items {}
- foreach name [array names fileset::infoTypes] {
- set val [set fileset::infoTypes($name)]
- if {[lindex $val 0] == "*"} {
- # always attached
- continue
- } else {
- lappend items $name [fileset::isAttached $fset $name]
- }
- }
- if {![llength $items]} {
- alertnote "No items exist which can be attached"
- return
- }
- set x 10
- set y 10
- eval lappend dialog [dialog::text "Checked items are currently attached" 10 y]
- incr y 10
- newforeach {name val} $items {
- eval lappend dialog [dialog::checkbox $name $val 10 y]
- }
- incr y 10
- set dialog [concat [dialog::okcancel 10 y] $dialog]
- set res [eval [list dialog -w 360 -h $y] $dialog]
- if {[lindex $res 1]} {
- return "Cancelled"
- }
- set count 2
- global fileset::attachments
- set mod 0
- newforeach {name val} $items {
- set newval [lindex $res $count]
- if {$newval != $val} {
- set mod 1
- if {$newval} {
- set fileset::attachments($fset,$name) 1
- } else {
- unset fileset::attachments($fset,$name)
- }
- prefs::modified fileset::attachments($fset,$name)
- }
- incr count
- }
- if {$mod} {
- alertnote "The next time you use the 'Edit Filesets' or \
- 'Edit A Fileset' dialog, these changes will take effect."
- }
- return "Done"
- }
-
- proc fileset::informationAttached {fset} {
- global fileset::infoTypes
- set res {}
- foreach name [array names fileset::infoTypes] {
- set val [set fileset::infoTypes($name)]
- if {[lindex $val 0] == "*" || [fileset::isAttached $fset $name]} {
- lappend res [lrange $val 1 end]
- }
- }
- return $res
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "fileset::attachAdditionalInformation" --
- #
- # Attach a piece of information which is only shown to the user if
- # the fileset's $toInfoName has the current value $toVal. Note,
- # however, that the information is actually attached to all filesets,
- # it is only the visibility to the user that is adjusted.
- # -------------------------------------------------------------------------
- ##
- proc fileset::attachAdditionalInformation {toInfoName toVal infoType name {defaultValue ""} {help ""} {modifiedScript ""}} {
- global fileset::infoAdditionalTypes gfileSets fileset::infoStorage
- lappend fileset::infoAdditionalTypes($toInfoName,$toVal) [list $name $infoType $defaultValue $help $modifiedScript]
- foreach fset [array names gfileSets] {
- if {![info exists fileset::infoStorage($fset,$name)]} {
- set fileset::infoStorage($fset,$name) $defaultValue
- if {[string length $modifiedScript]} {
- eval $modifiedScript [list $fset $defaultValue]
- }
- }
- }
- }
-
- proc fileset::additionalInformationAttached {fset infoName val} {
- global fileset::infoAdditionalTypes
- if {[info exists fileset::infoAdditionalTypes($infoName,$val)]} {
- return [set fileset::infoAdditionalTypes($infoName,$val)]
- }
- return ""
- }
-
- proc fileset::makeInfoDialogItems {fset yy} {
- upvar $yy y
- set res {}
- foreach pair [fileset::informationAttached $fset] {
- set name [lindex $pair 0]
- set infoType [lindex $pair 1]
- set val [fileset::getInformation $fset $name]
- eval lappend res [dialog::makeItem $infoType 20 y $fset $name $val]
- }
- set res
- }
-
- proc fileset::getInfoDialogItems {fset} {
- set res {}
- foreach pair [fileset::informationAttached $fset] {
- set name [lindex $pair 0]
- set infoType [lindex $pair 1]
- set val [fileset::getInformation $fset $name]
- lappend res [list $infoType $name $val [lindex $pair 3]]
- foreach additionalPair [fileset::additionalInformationAttached $fset $name $val] {
- set name [lindex $additionalPair 0]
- set infoType [lindex $additionalPair 1]
- set val [fileset::getInformation $fset $name]
- lappend res [list $infoType $name $val [lindex $additionalPair 3]]
- }
- }
- set res
- }
-
- proc fileset::setInfoFromDialog {fset count res} {
- foreach pair [fileset::informationAttached $fset] {
- set name [lindex $pair 0]
- set infoType [lindex $pair 1]
- set oldVal [fileset::getInformation $fset $name]
- set val [lindex $res $count]
- fileset::setInformation $fset $name $val
- incr count
- foreach additionalPair [fileset::additionalInformationAttached $fset $name $oldVal] {
- set name [lindex $additionalPair 0]
- set infoType [lindex $additionalPair 1]
- set val [lindex $res $count]
- fileset::setInformation $fset $name $val
- incr count
- }
- }
- return $count
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "fileset::listTypes" --
- #
- # Return sorted list of all fileset types currently registered. This
- # is used, for example, when creating a new fileset, so that we may
- # ask the user to select the type of fileset they wish to create.
- # -------------------------------------------------------------------------
- ##
- proc fileset::listTypes {} {
- global fileset::typeKindMap
- lsort -ignore [array names fileset::typeKindMap]
- }
-
- proc fileset::getKind {type} {
- global fileset::typeKindMap
- set fileset::typeKindMap($type)
- }
-
- proc fileset::getKindFromFset {fset} {
- global gfileSetsType fileset::typeKindMap
- set fileset::typeKindMap($gfileSetsType($fset))
- }
-
- proc fileset::getInformation {fset infoName} {
- global fileset::infoStorage
- if {![info exists fileset::infoStorage($fset,$infoName)]} {
- # This should only happen if we create a fileset manually
- # in Tcl by filling in the various array entries.
- fileset::ensureAllInfoAttached [list $fset] [list]
- }
- set fileset::infoStorage($fset,$infoName)
- }
-
- proc fileset::setInformation {fset infoName value} {
- global fileset::infoStorage fileset::infoTypes
- if {[info exists fileset::infoStorage($fset,$infoName)]} {
- if {[set fileset::infoStorage($fset,$infoName)] != $value} {
- set fileset::infoStorage($fset,$infoName) $value
- prefs::modified fileset::infoStorage($fset,$infoName)
- # This only fails for 'additional information'
- if {[info exists fileset::infoTypes($infoName)]} {
- set modifiedScript [lindex [set fileset::infoTypes($infoName)] 5]
- if {[string length $modifiedScript]} {
- eval $modifiedScript [list $fset $value]
- }
- }
- }
- } else {
- set fileset::infoStorage($fset,$infoName) $value
- }
- }
-
- proc fileset::checkOpeningPreference {name} {
- if {[hook::anythingRegistered fileset-file-opening]} {
- set fset [fileset::findForFile $name]
- if {[string length $fset]} {
- hook::callAll fileset-file-opening * $fset $name
- }
- }
- }
-
- proc fileset::checkCurrent {{win ""}} {
- return [fileset::findForFile $win]
- }
-
- proc fileset::relativePath {{win ""}} {
- set fset [fileset::findForFile $win]
- global gfileSets
- set root [file dirname $gfileSets($fset)]
- if {[file::pathStartsWith $win $root relative]} {
- return $relative
- } else {
- error "Not relative"
- }
- }
-
- proc fileset::canEdit {fset} {
- global gfileSetsType
- set type $gfileSetsType($fset)
- if {$type == "procedural"} {
- return -1
- }
- if {[info commands fileset::${type}::getDialogItems] != ""} {
- return 1
- } else {
- return [auto_load fileset::${type}::getDialogItems]
- }
- }
-
- proc filesetRegisterProcedural {name proc} {
- global gfileSets gfileSetsType
- set gfileSets($name) $proc
- set gfileSetsType($name) "procedural"
- }
-
-
- # ◊◊◊◊ Basic procedures ◊◊◊◊ #
-
- proc editFilesets {} {
- global currFileSet gfileSetsType gfileSets
-
- # Used to build up the items in the dialog
- set dialog {}
- # Used to store the original values of each fileset entry
- set values {}
- # Create a page for each editable fileset
- foreach fset [lsort -ignore [array names gfileSets]] {
- set canEdit [fileset::canEdit $fset]
- if {$canEdit < 0} {continue}
- set page [list $fset]
- if {$canEdit} {
- set thisfset_items [fileset::$gfileSetsType($fset)::getDialogItems $fset]
- eval lappend page $thisfset_items
- # Store a list of the current values
- set vals [list]
- foreach item $thisfset_items {
- lappend vals [lindex $item 2]
- }
- lappend values $vals
- }
- eval lappend page [fileset::getInfoDialogItems $fset]
- lappend dialog $page
- }
- set res [eval [list dialog::make -title "Edit filesets" -defaultpage $currFileSet] $dialog]
- # Now set everything
- set stored_index 0
- foreach fset [lsort -ignore [array names gfileSets]] {
- set canEdit [fileset::canEdit $fset]
- if {$canEdit < 0} {continue}
- set count 0
- if {$canEdit} {
- set vals [lindex $values $stored_index]
- set count [llength $vals]
- set mod 0
- for {set i 0} {$i < $count} {incr i} {
- if {[lindex $vals $i] != [lindex $res $i]} {
- set mod 1
- break
- }
- }
- # Only call the 'setDetails' proc if the fileset has changed.
- if {$mod} {
- message "Updating fileset $fset"
- eval fileset::$gfileSetsType($fset)::setDetails [list $fset] [lrange $res 0 [expr {$count -1}]]
- updateAFileset $fset
- }
- incr stored_index
- }
- set count [fileset::setInfoFromDialog $fset $count $res]
- set res [lrange $res $count end]
- }
- message "Fileset changes complete"
- }
-
- proc editAFileset {{fset ""}} {
- if {[catch {pickFileset $fset "Edit which fileset?" editable} fset]} {return}
- global currFileSet gfileSetsType gfileSets
- # Used to build up the items in the dialog
- set dialog {}
- # Used to store the original values of each fileset entry
- set values {}
- set page [list $fset]
- set thisfset_items [fileset::$gfileSetsType($fset)::getDialogItems $fset]
- eval lappend page $thisfset_items
- # Store a list of the current values
- set vals [list]
- foreach item $thisfset_items {
- lappend vals [lindex $item 2]
- }
- lappend values $vals
- eval lappend page [fileset::getInfoDialogItems $fset]
- lappend dialog $page
- set res [eval [list dialog::make -title "Edit '$fset' fileset" \
- -addbuttons [list "Attach/detach info" "Click here to attach or detach optional\
- additional information to this fileset" [list fileset::chooseAttachments $fset]]] $dialog]
- # Now set everything
- set stored_index 0
- set count 0
- set vals [lindex $values $stored_index]
- set count [llength $vals]
- set mod 0
- for {set i 0} {$i < $count} {incr i} {
- if {[lindex $vals $i] != [lindex $res $i]} {
- set mod 1
- break
- }
- }
- # Only call the 'setDetails' proc if the fileset has changed.
- if {$mod} {
- message "Updating fileset $fset"
- eval fileset::$gfileSetsType($fset)::setDetails [list $fset] [lrange $res 0 [expr {$count -1}]]
- updateAFileset $fset
- message "Fileset $fset updated"
- }
- incr stored_index
- set count [fileset::setInfoFromDialog $fset $count $res]
- set res [lrange $res $count end]
- }
-
- proc newFileset {{type ""}} {
- global currFileSet gfileSetsType
- if {$type == ""} {
- foreach ty [fileset::listTypes] {
- lappend types [quote::Prettify $ty]
- }
- set type [dialog::optionMenu "New fileset type?" \
- $types "From Directory"]
- foreach ty [fileset::listTypes] {
- if {[quote::Prettify $ty] == $type} {
- set type $ty
- break
- }
- }
- }
- set name [eval fileset::${type}::create]
-
- if {![string length $name]} return
-
- set gfileSetsType($name) $type
- prefs::modified gfileSets($name)
-
- set currFileSet $name
- hook::callAll fileset-new $gfileSetsType($name) $name
- return $currFileSet
- }
-
- proc deleteFileset {{fset ""} {yes 0}} {
- global currFileSet
-
- if {[catch {pickFileset $fset "Delete which Fileset?" "notbuiltin"} fset]} {
- return
- }
- if {$fset == ""} {
- message "The existing filesets cannot be deleted."
- return
- }
-
- global fileSets gfileSets fileSetsExtra gfileSetsType
-
- if {$yes || [dialog::yesno "Delete fileset \"$fset\"?"]} {
- set type $gfileSetsType($fset)
-
- hook::callAll fileset-delete $type $fset
-
- fileset::uncache $fset
- catch {unset "fileSetsExtra($fset)"}
- catch {unset "gfileSetsType($fset)"}
- catch {unset "fileSets($fset)"}
- catch {unset "gfileSets($fset)"}
-
- # fileSets($fset) is stored not in the usual prefs location,
- # but rather in the fsMenu2.0 cache, so we don't need to
- # call prefs::modified with it.
- prefs::modified gfileSetsType($fset) gfileSets($fset) \
- fileSetsExtra($fset)
- message "The fileset \"$fset\" has been deleted"
-
- if {$currFileSet == $fset} {
- set old $currFileSet
- catch {
- set currFileSet [lindex [array names gfileSets] 0]
- }
- hook::callAll fileset-current * $old $currFileSet
- }
- }
- }
-
- proc renameFileset {} {
- global fileSets gfileSets currFileSet fileSetsExtra gfileSetsType
-
- if {[catch {pickFileset "" "Fileset to rename?" "notbuiltin"} fset]} {return}
- if {$fset == ""} {
- message "The existing filesets cannot be renamed."
- return
- }
-
- set name [getline "Rename to:" $fset]
- if {![string length $name] || $name == $fset} return
-
- set gfileSets($name) $gfileSets($fset)
- set gfileSetsType($name) $gfileSetsType($fset)
- prefs::modified gfileSets($name) gfileSetsType($name)
-
- if {[info exists fileSets($fset)]} {
- set fileSets($name) $fileSets($fset)
- }
- if {[info exists fileSetsExtra($fset)]} {
- set fileSetsExtra($name) $fileSetsExtra($fset)
- prefs::modified fileSetsExtra($name)
- }
-
- deleteFileset $fset 1
-
- set currFileSet $name
- hook::callAll fileset-new $gfileSetsType($name) $name
- message "The fileset \"$fset\" has been renamed to \"$name\""
- }
-
- proc updateCurrentFileset {} {
- global currFileSet
- updateAFileset $currFileSet
- }
-
- proc updateAFileset { {fset ""} } {
- global gfileSetsType
-
- if {[catch {pickFileset $fset} fset]} {return}
-
- fileset::uncache $fset
- fileset::make $fset 1
- }
-
- proc fileset::uncache {fset} {
- global fsMenuCache
- if {[info exists fsMenuCache($fset)]} {
- unset fsMenuCache($fset)
- cache::add fsMenu2.0 "eval" [list unset fsMenuCache($fset)]
- }
- hook::callAll fileset-uncache * $fset
- }
-
- proc getFilesInSet {fset} {
- global gfileSets gfileSetsType
- switch -- [fileset::getKind $gfileSetsType($fset)] {
- "list" {
- return $gfileSets($fset)
- }
- "glob" {
- global filesetmodeVars fileSetsExtra
- if {$filesetmodeVars(includeNonTextFiles)} {
- set l [glob -nocomplain -dir [file dirname "$gfileSets($fset)"] -- [file tail "$gfileSets($fset)"]]
- if {[info exists fileSetsExtra($fset)]} {
- foreach pat $fileSetsExtra($fset) {
- foreach f [glob -nocomplain -dir [file dirname "$gfileSets($fset)"] -- $pat] {
- set i [lsearch $l $f]
- set l [lreplace $l $i $i]
- }
- }
- }
- return $l
- } else {
- set l [glob -types TEXT -nocomplain -dir [file dirname "$gfileSets($fset)"] -- [file tail "$gfileSets($fset)"]]
- if {[info exists fileSetsExtra($fset)]} {
- foreach pat $fileSetsExtra($fset) {
- foreach f [glob -types TEXT -nocomplain -dir [file dirname "$gfileSets($fset)"] -- $pat] {
- set i [lsearch $l $f]
- set l [lreplace $l $i $i]
- }
- }
- }
- return $l
- }
- }
- "procedural" {
- switch -- $gfileSetsType($fset) {
- "recurseIn" {
- return [file::recurse [file dirname $gfileSets($fset)]]
- }
- default {
- return [$gfileSets($fset)]
- }
- }
- }
- "default" {
- global fileSets
- return $fileSets($fset)
- }
- }
- }
-
- proc fileset::make {name andMenu} {
- if {$andMenu} {
- global fsMenuCache
- if {[info exists fsMenuCache($name)]} {
- set m [set fsMenuCache($name)]
- if {[llength $m]} { return $m }
- }
- }
-
- global gfileSetsType fileSets
- if {[info exists gfileSetsType($name)]} {
- set type $gfileSetsType($name)
- message "Building ${name}…"
- if {![catch {fileset::${type}::updateContents $name $andMenu} m]} {
- if {[llength $m]} {
- fileset::cacheMenu $name $m
- }
- if {[info exists fileSets($name)]} {
- cache::add fsMenu2.0 "variable" fileSets($name)
- }
- hook::callAll fileset-update $type $name $m
- message "Building ${name}… complete"
- return $m
- } else {
- # nothing
- }
- }
- return [list]
- }
-
- proc fileset::cacheMenu {fset m} {
- if {[llength $m]} {
- global fsMenuCache
- set fsMenuCache($fset) $m
- cache::add fsMenu2.0 variable fsMenuCache($fset)
- }
- }
-
- # Called in response to user changing filesets manually
- proc changeFileSet {item} {
- global currFileSet tagFile
- if {$currFileSet != $item} {
- set old $currFileSet
- set currFileSet $item
- hook::callAll fileset-current * $old $currFileSet
- }
- # Bring in the tags file for this fileset
- set fname [tagFileName]
- if {[file exists $fname]} {
- if {[dialog::yesno "Use tag file from folder \"$dir\" ?"]} {
- set tagFile $fname
- }
- }
- }
-
- # ◊◊◊◊ Open an item in a fileset ◊◊◊◊ #
-
- proc fileset::openItemProc {fset parent item} {
- global gfileSetsType
- if {$fset != ""} {set m $fset} else { set m $parent}
- # try a type-specific method first
- set proc fileset::$gfileSetsType($m)::selected
- if {[info commands $proc] == "" && (![auto_load $proc])} {
- # if that failed then just hope it's an ordinary list
- if {![catch {filesetBasicOpen $m $item} err]} {return}
- } else {
- if {[llength [info args $proc]] == 2} {
- if {![catch {eval [list $proc $parent $item]} err]} {return}
- } else {
- if {![catch {eval [list $proc $fset $parent $item]} err]} {return}
- }
- }
-
- fileset::fileNotFound $fset $err
- }
-
- proc fileset::fileNotFound {fset {text ""}} {
- if {[string length $text]} {
- append text "\r"
- }
- append text "That file wasn't found. The fileset may be out of date."
- if {![catch {dialog::yesno -y "Rebuild fileset" \
- -n "Edit fileset" -c $text} res]} {
- if {$res} {
- updateAFileset $fset
- } else {
- editAFileset $fset
- }
- return 1
- } else {
- message "Cancelled"
- return 0
- }
- }
-
- proc filesetBasicOpen {fset item} {
- set f [file::pathEndsWith $item [getFilesInSet $fset]]
- if {[string length $f]} {
- autoUpdateFileset $fset
- file::openAny $f
- return
- }
- error "File for selected '$item' not found"
- }
-
- proc autoUpdateFileset { name } {
- global currFileSet filesetmodeVars
- if {$filesetmodeVars(autoAdjustFileset)} {
- changeFileSet $name
- }
- }
-
- # ◊◊◊◊ Query procs ◊◊◊◊ #
-
- proc fileset::findForFile { {win ""} } {
- if {$win == ""} { set win [win::Current] }
- global currFileSet gfileSets gfileSetsType
- foreach fset [concat [list $currFileSet] [array names gfileSets]] {
- switch -- [fileset::getKind $gfileSetsType($fset)] {
- "list" {
- if {[lsearch -exact $gfileSets($fset) $win] != -1} {
- return $fset
- }
- }
- "glob" {
- if {[file::pathStartsWith $win [file dirname $gfileSets($fset)]]} {
- return $fset
- }
- }
- "procedural" {
- switch -- $gfileSetsType($fset) {
- "recurseIn" {
- if {[file::pathStartsWith $win [file dirname $gfileSets($fset)]]} {
- return $fset
- }
- }
- default {
- continue
- }
- }
- }
- "fromHierarchy" {
- if {[file::pathStartsWith $win [file dirname [lindex $gfileSets($fset) 0]]]} {
- return $fset
- }
- }
- "default" {
- # Not sure what this is for in this procedure.
- global fileSets
- if {[lsearch -exact $fileSets($fset) $win] != -1} {
- return $fset
- }
- }
- }
- }
- return ""
- }
-
- proc dirtyFileset { fset } {
- foreach f [getFilesInSet $fset] {
- if { ![catch {getWinInfo -w $f arr}] && $arr(dirty)} { return 1 }
- }
- return 0
- }
-
- proc isWindowInFileset { {win "" } {type ""} } {
- if {$win == ""} { set win [win::Current] }
- global currFileSet gfileSets gfileSetsType
-
- if { $type == "" } {
- set okSets [array names gfileSets]
- } else {
- set okSets {}
- foreach s [array names gfileSets] {
- if { $gfileSetsType($s) == $type } {
- lappend okSets $s
- }
- }
- }
-
- if {[array exists gfileSets]} {
- if {[lsearch -exact $okSets $currFileSet] != -1 } {
- # check current fileset
- if {[isWindowInFilelist $win [getFilesInSet $currFileSet]]} {
- # we're set, it's in this fileset
- return $currFileSet
- }
- }
-
- # check other fileset
- foreach fset $okSets {
- if {[isWindowInFilelist $win [getFilesInSet $fset]]} {
- # we're set, it's in this project
- return $fset
- }
- }
- }
- return ""
-
- }
-
- if {[info tclversion] < 8.0} {
- proc isWindowInFilelist { win flist } {
- set win [win::StripCount $win]
- if {[lsearch -exact $flist $win] != -1 } {
- return 1
- } else {
- return 0
- }
- }
- } else {
- proc isWindowInFilelist { win flist } {
- set win [win::StripCount $win]
- foreach f $flist {
- if {[string equal $win [file::ensureStandardPath $f]]} {
- return 1
- }
- }
- return 0
- }
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "pickFileset" --
- #
- # Ask the user for a/several filesets. If 'fset' is set, we just return
- # that (this avoids 'if {$fset != ""} { set fset [pick...] } constructs
- # everywhere). A prompt can be given, and a dialog type (either a
- # listpick, a pop-up menu, or a listpick with multiple selection), and
- # extra items can be added to the list if desired.
- # -------------------------------------------------------------------------
- ##
- proc pickFileset { fset {prompt Fileset?} {type "list"} {extras {}} } {
- global gfileSets currFileSet
- if {[array size gfileSets] == 0} {
- error "There are no filesets currently defined"
- }
- if { $fset != "" } { return $fset }
- switch -- $type {
- "popup" {
- set fset [eval [list prompt $prompt \
- $currFileSet "FileSet:"] [lsort -ignore [array names gfileSets]]]
- if {![info exists gfileSets($fset)]} { error "No such fileset" }
- return $fset
- }
- "list" {
- return [listpick -p $prompt -L $currFileSet \
- [lsort -ignore [concat $extras [array names gfileSets]]]]
- }
- "multilist" {
- return [listpick -p $prompt -l -L $currFileSet \
- [lsort -ignore [concat $extras [array names gfileSets]]]]
- }
- "notbuiltin" {
- global fileset::notChangeable
- set choices [list]
- foreach fset [lsort -ignore [array names gfileSets]] {
- if {[lsearch -exact ${fileset::notChangeable} $fset] == -1} {
- lappend choices $fset
- }
- }
- if {[llength $choices]} {
- set item $currFileSet
- if {[lsearch -exact $choices $currFileSet] == -1} {
- set item [lindex $choices 0]
- }
- return [listpick -p $prompt -L $item \
- [lsort -ignore [concat $extras $choices]]]
- } else {
- return ""
- }
- }
- "editable" {
- set choices [list]
- foreach fset [lsort -ignore [array names gfileSets]] {
- set canEdit [fileset::canEdit $fset]
- if {$canEdit < 0} {continue}
- lappend choices $fset
- }
- if {[llength $choices]} {
- set item $currFileSet
- if {[lsearch -exact $choices $currFileSet] == -1} {
- set item [lindex $choices 0]
- }
- return [listpick -p $prompt -L $item \
- [lsort -ignore [concat $extras $choices]]]
- } else {
- return ""
- }
- }
- }
- }
-
- # ◊◊◊◊ Tags ◊◊◊◊ #
-
- if {![string length [info commands alphaFindTag]]} {
- rename findTag alphaFindTag
- rename createTagFile alphaCreateTagFile
- }
-
- proc tagFileName {} {
- global gfileSets currFileSet
- return [file join [file dirname [lindex $gfileSets($currFileSet) 0]] "[join ${currFileSet}]TAGS"]
- }
-
- proc findTag {} {
- global gfileSetsType currFileSet
- # try a type-specific method first
- if {[catch {fileset::$gfileSetsType($currFileSet)::findTag}]} {
- alphaFindTag
- }
- }
-
- proc createTagFile {} {
- global gfileSetsType currFileSet tagFile modifiedVars
- set tagFile [tagFileName]
- lappend modifiedVars tagFile
-
- # try a type-specific method first
- if {[catch {fileset::$gfileSetsType($currFileSet)::createTagFile}]} {
- alphaCreateTagFile
- }
- }
-
- # ◊◊◊◊ Called by Alpha's core ◊◊◊◊ #
-
- # Called from Alpha to get list of files for current file set.
- proc getCurrFileSet {} {
- global currFileSet
- return [getFileSet $currFileSet]
- }
-
- # Called from Alpha to get names. The first name returned is taken to
- # be the current fileset. For Alpha < 8.0, the list returned contains
- # the first item twice (as the first item, and then in its correct
- # position in the list). For Alpha >= 8.0 this silly behaviour has
- # been removed.
- proc getFileSetNames {{ordered 0}} {
- global gfileSets currFileSet gDirScan
- set perm {}
- if {!$ordered && $currFileSet != ""} {
- lappend perm $currFileSet
- }
- foreach n [lsort -ignore [array names gfileSets]] {
- if {!$ordered && ([info tclversion] >= 8.0) && $n == $currFileSet} {continue}
- if {[info exists gDirScan($n)]} {
- lappend temp $n
- } else {
- lappend perm $n
- }
- }
- if {[info exists temp]} {
- return [concat $perm - $temp]
- } else {
- return $perm
- }
- }
-
- #================================================================================
- # Edit a file from a fileset via list dialogs (no mousing around).
- #================================================================================
-
- namespace eval file {}
-
- proc file::openViaFileset {{fset ""}} {
- global currFileSet gfileSetsType file::separator
-
- if {[catch {pickFileset $fset {Fileset?} "list"} fset]} {return}
- set currFileSet $fset
-
- if {[info tclversion] < 8.0} {
- set cmd fileset::$gfileSetsType($fset)::getRoot
- } else {
- set cmd ::fileset::$gfileSetsType($fset)::getRoot
- }
- if {[llength [info commands $cmd]] || [auto_load $cmd]} {
- set filename [fileset::$gfileSetsType($fset)::getRoot $fset]
- while {[file isdirectory $filename]} {
- set disp [list]
- foreach f [glob -dir $filename *] {
- lappend disp [file tail $f]
- }
- set disp [concat [list ..] [lsort -ignore $disp]]
- if {[catch {listpick -p {File?} $disp} choice]} {return}
- if {$choice == ".."} {
- set filename [file dirname $filename]
- } else {
- set filename [file join $filename $choice]
- }
- }
- edit $filename
- } else {
- set allfiles [getFilesInSet $fset]
- foreach f $allfiles {
- lappend disp [file tail $f]
- }
- if {[catch {listpick -l -p {File?} [lsort -ignore $disp]} files]} {return}
- foreach res $files {
- set ind [lsearch -glob $allfiles "\*${file::separator}[quote::Find $res]"]
- fileset::openItemProc $fset "" [lindex $allfiles $ind]
- }
- }
- }
-
- # We only return TEXT files, since we don't want Alpha
- # manipulating the data fork of non-text files.
- proc getFileSet {fset} {
- global filesetmodeVars
- if {$filesetmodeVars(includeNonTextFiles)} {
- set fnames ""
- foreach f [getFilesInSet $fset] {
- if {[file isfile $f]} {
- getFileInfo $f a
- if {$a(type) == "TEXT"} {
- lappend fnames $f
- }
- }
- }
- return $fnames
- } else {
- return [getFilesInSet $fset]
- }
- }
-
-